﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
using System.Drawing.Drawing2D;

namespace wChess.ChessPieces
{
    /// <summary>
    /// 红黑方枚举
    /// </summary>
    public enum TEAM
    {
        RED,
        BLACK
    }
    /// <summary>
    /// 该棋子是棋盘上边的一方，还是下边的一方
    /// </summary>
    public enum SIDE
    {
        UP,
        DOWN
    }
    public abstract class ChessPiece
    {
        /// <summary>
        /// 棋子名称
        /// </summary>
        public string Name { set; get; }

        /// <summary>
        /// 当前棋子下一步可以移动的位置集合
        /// </summary>
        public abstract List<Point> NextSteps { get; }

        /// <summary>
        /// 该棋子最近一次移动是否吃过对方的棋子
        /// </summary>
        private bool eatenTag = false;

        private Stack<PieceInfo> _HistorySteps = new Stack<PieceInfo>();
        /// <summary>
        /// 用堆栈记录下该棋子的历史位置轨迹
        /// </summary>
        public Stack<PieceInfo> HistorySteps
        {
            get
            {
                return _HistorySteps;
            }
            set
            {
                _HistorySteps = value;
            }
        }

        private Stack<ChessPiece> _EatenPieces = new Stack<ChessPiece>();

        /// <summary>
        /// 被该棋子吃掉的棋子
        /// </summary>
        public Stack<ChessPiece> EatenPieces
        {
            get
            {
                return _EatenPieces;
            }
            set
            {
                _EatenPieces = value;
            }
        }

        private Point _FloatMetaPosition;

        /// <summary>
        /// 棋子移动过程中未放下状态的位置
        /// </summary>
        public Point FloatMetaPosition
        {
            get
            {
                return _FloatMetaPosition;
            }
            set
            {
                _FloatMetaPosition = value;
            }
        }

        /// <summary>
        /// 当前棋子是否被鼠标按下
        /// </summary>
        public bool IsActive { set; get; }

        /// <summary>
        /// 是否已经被标记为“假吃掉”，该属性用于获取下一步着法
        /// </summary>
        public bool IsVirtualEaten { set; get; }

        /// <summary>
        /// 棋子落地后的位置
        /// </summary>
        public Point FixedMetaPosition { set; get; }

        /// <summary>
        /// 走棋之前的上一个位置
        /// </summary>
        public Point OldMetaPosition { set; get; }

        /// <summary>
        /// 红方还是黑方
        /// </summary>
        public TEAM Team { set; get; }

        /// <summary>
        /// 是棋盘上方的棋子，还是下方的棋子
        /// </summary>
        public SIDE Side { set; get; }

        private float Left
        {
            get
            {
                return (ChessBoard.LINE_WEIGHT + ChessBoard.sW) * FloatMetaPosition.X + ChessBoard.LINE_WEIGHT / 2;
                //return (ChessBoard.LINE_WEIGHT + ChessBoard.sW) * FixedMetaPosition.X + ChessBoard.LINE_WEIGHT / 2;
            }
        }

        private float Top
        {

            get
            {
                return (ChessBoard.LINE_WEIGHT + ChessBoard.sH) * FloatMetaPosition.Y + ChessBoard.LINE_WEIGHT / 2;
                //return (ChessBoard.LINE_WEIGHT + ChessBoard.sH) * FixedMetaPosition.Y + ChessBoard.LINE_WEIGHT / 2;
            }
        }

        private float CenterX
        {
            get
            {
                return Left + ChessBoard.sW / 2;
            }
        }

        private float CenterY
        {
            get
            {
                return Top + ChessBoard.sH / 2;
            }
        }

        private Brush FontBrush
        {
            get
            {
                Brush r = null;
                switch (Team)
                {
                    case TEAM.BLACK:
                        r = new SolidBrush(Color.Black);
                        break;
                    case TEAM.RED:
                        r = new SolidBrush(Color.Red);
                        break;
                }
                return r;
            }
        }

        /// <summary>
        /// 是否显示下步预览
        /// </summary>
        public bool ShowPreview { set; get; }

        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="name">棋子名称</param>
        /// <param name="floatMetaPosition">索引坐标</param>
        /// <param name="team">红黑方</param>
        public ChessPiece(string name, Point floatMetaPosition, TEAM team = TEAM.RED, SIDE side = SIDE.UP)
        {
            Name = name;
            Team = team;
            Side = side;

            FloatMetaPosition = floatMetaPosition;
            FixedMetaPosition = FloatMetaPosition;
            OldMetaPosition = new Point(-1, -1);

            //压栈
            HistorySteps.Push(new PieceInfo { Pos = FixedMetaPosition, EatenTag = eatenTag });

            //设置棋盘矩阵
            ChessBoard.Matrix[FloatMetaPosition.Y, FloatMetaPosition.X] = 1;
        }

        /// <summary>
        /// 当放下当前棋子时，更新棋盘矩阵对应位置的值
        /// </summary>
        public void UpdateMatrix()
        {
            ChessBoard.Matrix[FloatMetaPosition.Y, FloatMetaPosition.X] = 1;
            if (FloatMetaPosition != FixedMetaPosition)
                ChessBoard.Matrix[FixedMetaPosition.Y, FixedMetaPosition.X] = 0;
        }

        /// <summary>
        /// 移除自身
        /// </summary>
        public void Remove()
        {
            ChessBoard.Pieces.Remove(this);
        }

        /// <summary>
        /// 绘制当前棋子可以移动的位置
        /// </summary>
        /// <param name="g">Graphics对象</param>
        public void DrawPreview(Graphics g)
        {
            foreach (var next in NextSteps)
            {
                var pos = ChessBoard.GetPixelPosFromMetaPos(next);
                Pen pen = new Pen(Color.Green, 2);
                g.DrawEllipse(pen, pos.Item1 - ChessBoard.sW / 2, pos.Item2 - ChessBoard.sH / 2, ChessBoard.sW, ChessBoard.sH);
            }
        }

        /// <summary>
        /// 绘制自身
        /// </summary>
        /// <param name="g">Graphics对象</param>
        public void Draw(Graphics g)
        {
            g.DrawImage(new Bitmap(global::wChess.Properties.Resources.piece), Left,
                Top,
                ChessBoard.sW,
                ChessBoard.sH);

            #region 用形状绘制棋子
            //Brush brush = new SolidBrush(Color.White);
            //g.FillEllipse(brush,
            //    Left,
            //    Top,
            //    ChessBoard.sW,
            //    ChessBoard.sH); 
            #endregion

            //绘制选中状态
            if (IsActive)
            {
                //代表选中状态的圆形比棋子大的像素数
                int activeBorder = 5;

                g.DrawEllipse(Pens.Red,
                    Left - activeBorder,
                    Top - activeBorder,
                    ChessBoard.sW + activeBorder * 2,
                    ChessBoard.sH + activeBorder * 2);
            }

            //绘制当前棋子可以移动的位置
            if (ShowPreview)
            {
                DrawPreview(g);
            }

            Font font = new Font("楷体", 25);
            var fontSize = g.MeasureString(Name, font);
            g.DrawString(Name, font, FontBrush, CenterX - fontSize.Width / 2, CenterY - fontSize.Height / 2 + 5);
        }

        /// <summary>
        /// 判断指定点是否位于当前棋子的圆形内
        /// </summary>
        /// <param name="mousePosition">鼠标位置</param>
        /// <returns></returns>
        public bool IsMouseEnter(Point mousePosition)
        {
            double l = Math.Sqrt(Math.Pow(mousePosition.X - CenterX, 2) + Math.Pow(mousePosition.Y - CenterY, 2));
            return l < ChessBoard.sW / 2;
            //if (mousePosition.X > Left && mousePosition.X < Left + ChessBoard.sW && mousePosition.Y > Top && mousePosition.Y < Top + ChessBoard.sH)
            //    return true;
            //return false;
        }

        /// <summary>
        /// 吃指定像素坐标处的棋子，
        /// 如果目标位置没有棋子、或者是当前棋子自身、或者是己方棋子，则不吃
        /// </summary>
        /// <param name="enemyPixcelPt">对方棋子位置</param>
        public void Kill(Point enemyPixcelPt)
        {
            var pieces = ChessBoard.GetPieceFromPixelPos(enemyPixcelPt);

            //如果目标位置没有别的棋子，则设置eatenTag为false
            if (pieces.Count == 0)
            {
                eatenTag = false;
                return;
            }
            if (pieces.Count == 1 && pieces[0].GetHashCode() == this.GetHashCode())
            {
                //目标位置仅有一个棋子，且该棋子是当前棋子自身，
                //此时，也应该设置eatenTag为false
                //【此语句尤为重要】
                eatenTag = false;
                return;
            }
            foreach (var p in pieces)
            {
                //排除当前棋子本身（自己不能吃自己）
                if (p.GetHashCode() == this.GetHashCode())
                    continue;

                //排除己方棋子（不能吃己方棋子）
                if (p.Team == this.Team)
                {
                    this.FloatMetaPosition = this.FixedMetaPosition;
                    break;
                }

                //保存被该棋子吃掉的对方棋子
                EatenPieces.Push(p);

                //吃对方的棋子
                p.Remove();

                eatenTag = true;
            }
        }

        /// <summary>
        /// 悔棋
        /// </summary>
        public void RollBack()
        {
            //更新棋盘矩阵对应位置
            ChessBoard.Matrix[FixedMetaPosition.Y, FixedMetaPosition.X] = 0;

            //如果该棋子最近一次移动吃了别人的棋子，就吐出来；没有吃的话，则不管他
            if (eatenTag)
            {
                if (EatenPieces.Count > 0)
                {
                    //取到刚被吃掉的棋子
                    var eatenPiece = EatenPieces.Pop();

                    //恢复被吃掉的棋子
                    ChessBoard.Pieces.Add(eatenPiece);

                    //更新棋盘矩阵对于位置
                    ChessBoard.Matrix[eatenPiece.FixedMetaPosition.Y, eatenPiece.FixedMetaPosition.X] = 1;
                }
            }

            //取得该棋子移动前的位置
            var preStep = this.HistorySteps.Pop();
            FloatMetaPosition = preStep.Pos;
            FixedMetaPosition = preStep.Pos;

            var p = this.HistorySteps.Peek();
            OldMetaPosition = p.Pos;
            eatenTag = p.EatenTag;

            //更新棋盘矩阵对应位置
            ChessBoard.Matrix[FixedMetaPosition.Y, FixedMetaPosition.X] = 1;

            //更新玩家走棋标记
            ChessBoard.UpdatePlayToken();
        }

        /// <summary>
        /// 移动到指定位置（返回false说明当前还没有轮到己方走棋）
        /// </summary>
        /// <param name="pixelPos">目标位置</param>
        /// <returns></returns>
        public MOVE_RESULT MoveTo(Point pixelPos)
        {
            //目标位置的索引坐标
            var metaPos = ChessBoard.GetMetaPosFromPixelPos(pixelPos.X, pixelPos.Y);

            //移动前后位置相同，则不作处理
            if (metaPos == FixedMetaPosition)
                return MOVE_RESULT.NULL;

            //双方棋手应该轮流走棋
            //ChessBoard.PlayToken为true时，应该红方走；
            //ChessBoard.PlayToken为false时，应该黑方走；
            if ((ChessBoard.PlayToken == true && Team == TEAM.BLACK) || (ChessBoard.PlayToken == false && Team == TEAM.RED))
            {
                FloatMetaPosition = FixedMetaPosition;
                if (metaPos != this.FixedMetaPosition)
                    return MOVE_RESULT.ENEMY_TURN;
            }

            //判断能否移动到当前位置
            var canMove = CanMoveTo(metaPos);
            if (!canMove)
            {
                //如果不能移动到当前位置，则恢复原位
                FloatMetaPosition = FixedMetaPosition;
                return MOVE_RESULT.CAN_NOT_MOVE;
            }
            else
            {
                //如果能够移动到目标位置

                //更新玩家走棋标记
                ChessBoard.UpdatePlayToken();

                //吃对方棋子
                Kill(pixelPos);

                //当放下当前棋子时，更新棋盘矩阵对应位置的值
                UpdateMatrix();

                //使该棋子的历史轨迹进栈【必须放在语句#之前】
                HistorySteps.Push(new PieceInfo { Pos = FixedMetaPosition, EatenTag = eatenTag });

                //保存旧位置
                OldMetaPosition = FixedMetaPosition;

                //当前棋子移动到新位置后，更新FixedMetaPosition
                //#
                FixedMetaPosition = FloatMetaPosition;

                //保存当前棋局中所有走过的棋子记录
                ChessBoard.HistoryPieces.Push(this);

                //判断是否将军
                var beChecked = ChessBoard.CheckCheck();
                if (beChecked.Count > 0)
                {
                    if (IsCheckingMe(beChecked))
                    {
                        ChessBoard.RollBack();
                        return MOVE_RESULT.CHECK_SELF;
                    }
                    return MOVE_RESULT.CHECK_ENEMY;
                }

                //判断是否明将
                if (ChessBoard.CheckMingJiang())
                {
                    ChessBoard.RollBack();
                    return MOVE_RESULT.MING_JIANG;
                }
            }
            return MOVE_RESULT.NULL;
        }

        /// <summary>
        /// 交换某一棋子吃过的棋子位置，用于交换棋盘
        /// </summary>
        /// <param name="p"></param>
        public void ExchangeEatenPieces()
        {
            if (EatenPieces.Count > 0)
            {
                EatenPieces.UpdateStack<ChessPiece>(piece =>
                {
                    //更新这个被吃掉的棋子的坐标信息
                    piece.FixedMetaPosition = new Point(ChessBoard.BOARD_META_WIDTH - 1 - piece.FixedMetaPosition.X, ChessBoard.BOARD_META_HEIGHT - 1 - piece.FixedMetaPosition.Y);
                    piece.FloatMetaPosition = new Point(ChessBoard.BOARD_META_WIDTH - 1 - piece.FloatMetaPosition.X, ChessBoard.BOARD_META_HEIGHT - 1 - piece.FloatMetaPosition.Y);
                    piece.OldMetaPosition = new Point(ChessBoard.BOARD_META_WIDTH - 1 - piece.OldMetaPosition.X, ChessBoard.BOARD_META_HEIGHT - 1 - piece.OldMetaPosition.Y);

                    //更新这个被吃掉的棋子的历史轨迹【注意】
                    piece.HistorySteps.UpdateStack<PieceInfo>(pt =>
                    {
                        return new PieceInfo { Pos = new Point(ChessBoard.BOARD_META_WIDTH - 1 - pt.Pos.X, ChessBoard.BOARD_META_HEIGHT - 1 - pt.Pos.Y), EatenTag = pt.EatenTag };
                    });

                    //递归调用，更新这个被吃掉的棋子位置
                    piece.ExchangeEatenPieces();
                    return piece;
                });
            }
        }

        /// <summary>
        /// 判断己方是否被将军
        /// </summary>
        /// <param name="beChecked">被将军列表</param>
        /// <returns></returns>
        private bool IsCheckingMe(List<ChessPiece> beChecked)
        {
            bool r = false;
            if (beChecked == null)
                return false;
            foreach (var b in beChecked)
            {
                if (b.Team == this.Team)
                {
                    r = true;
                    break;
                }
            }
            return r;
        }

        /// <summary>
        /// 判断当前棋子是否能够移动到目标位置
        /// </summary>
        /// <param name="metaPt">目标位置</param>
        /// <returns></returns>
        public bool CanMoveTo(Point metaPt)
        {
            return NextSteps.Contains(metaPt);
        }


        /*
         * 方法CanMoveTo和属性NextSteps可以有两种实现方案：
         * （1）CanMoveTo设置为非抽象方法，NextSteps设置为抽象属性
         * （2）CanMoveTo设置为抽象方法，NextSteps设置为非抽象属性。具体实现思路是：
         *      遍历棋盘矩阵的每一个位置，调用CanMoveTo方法，看当前棋子能否移动到该位置，
         *      能的话就把该位置添加到NextSteps集合中，否则不添加。
         * 我们目前采用的是方案1。
         */
    }
}
